/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.apisupport; import java.awt.datatransfer.*; import java.io.*; import java.util.*; import java.util.jar.*; import javax.swing.SwingUtilities; import javax.swing.event.*; import org.openide.*; import org.openide.actions.*; import org.openide.cookies.*; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.modules.ModuleDescription; import org.openide.nodes.*; import org.openide.src.*; import org.openide.util.HelpCtx; import org.openide.util.WeakListener; import org.openide.util.actions.SystemAction; import org.openide.util.datatransfer.*; public class CategoryNode extends AbstractNode { /** * @associates String */ private static final Map categorySupertypes = new HashMap (); static { categorySupertypes.put (ModuleDescription.TAG_MAIN, "org.openide.modules.ModuleInstall"); categorySupertypes.put (ModuleDescription.SECTION_ACTION, "org.openide.util.actions.SystemAction"); categorySupertypes.put (ModuleDescription.SECTION_CLIPBOARD_CONVERTOR, "org.openide.util.datatransfer.ExClipboard$Convertor"); categorySupertypes.put (ModuleDescription.SECTION_DEBUGGER, "org.openide.debugger.Debugger"); categorySupertypes.put (ModuleDescription.SECTION_FILESYSTEM, "org.openide.filesystems.FileSystem"); categorySupertypes.put (ModuleDescription.SECTION_LOADER, "org.openide.loaders.DataLoader"); categorySupertypes.put (ModuleDescription.SECTION_NODE, "org.openide.nodes.Node"); categorySupertypes.put (ModuleDescription.SECTION_OPTION, "org.openide.options.SystemOption"); categorySupertypes.put (ModuleDescription.SECTION_SERVICE, "org.openide.ServiceType"); //categorySupertypes.put (ModuleDescription.SECTION_EXECUTOR, "org.openide.execution.ExecutorType"); } private ManifestProvider provider; private Object type; private int style; static final int STYLE_MAIN = 0; static final int STYLE_SECTION = 1; static final int STYLE_DEP = 2; public CategoryNode (ManifestProvider provider, Object type) { super (new CategoryChildren (provider, type)); this.provider = provider; this.type = type; if (type.equals (ModuleDescription.TAG_MAIN) || type.equals (ModuleDescription.TAG_DESCRIPTION)) style = STYLE_MAIN; else if (type.equals (ModuleDescription.TAG_MODULE_DEPENDENCIES) || type.equals (ModuleDescription.TAG_PACKAGE_DEPENDENCIES) || type.equals (ModuleDescription.TAG_JAVA_DEPENDENCIES) || type.equals (ModuleDescription.TAG_IDE_DEPENDENCIES)) style = STYLE_DEP; else style = STYLE_SECTION; getCategoryChildren ().style = style; setName (type.toString ()); // XXX nice to have shortDescription giving a brief explanation of what this category is for setIconBase ("/org/netbeans/modules/apisupport/resources/CategoryNodeIcon"); } public HelpCtx getHelpCtx () { return new HelpCtx ("org.netbeans.modules.apisupport.modules"); } private CategoryChildren getCategoryChildren () { return (CategoryChildren) getChildren (); } protected SystemAction[] createActions () { return new SystemAction[] { SystemAction.get (PasteAction.class), null, SystemAction.get (ToolsAction.class), SystemAction.get (PropertiesAction.class), }; } // XXX new types (for DepNode's) /** Test whether the indicated node can serve as the main thing to be pasted to this manifest section. * I.e., either a class assignable to the desired section class, or a data node for a .hs file. * @param n the node * @return the section name if it is, else null */ private String isAcceptablePrimaryPaster (Node n) { //System.err.println("Testing for paster: " + n.getDisplayName ()); if (type.equals (ModuleDescription.TAG_DESCRIPTION)) { DataObject dob = (DataObject) n.getCookie (DataObject.class); if (dob != null && dob.getPrimaryFile ().hasExt ("hs")) return dob.getPrimaryFile ().getPackageName ('.'); else return null; } else { InstanceCookie inst = (InstanceCookie) n.getCookie (InstanceCookie.class); if (inst == null) return null; try { Class clazz = inst.instanceClass (); String supeName = (String) categorySupertypes.get (type); Class supe = Class.forName (supeName); if (supe.isAssignableFrom (clazz)) return clazz.getName ().replace ('.', '/') + ".class"; else return null; } catch (Exception e) { e.printStackTrace (); return null; } } } protected void createPasteTypes (Transferable t, List l) { super.createPasteTypes (t, l); if (style == STYLE_MAIN && ! getCategoryChildren ().keys.isEmpty ()) { //System.err.println("will not accept >1 main instance"); return; // cannot have more than one } try { //System.err.println("transferable: " + t); // XXX this could be simplified with NodeTransfer.nodes... if (t.isDataFlavorSupported (ExTransferable.multiFlavor)) { //System.err.println("multi flavor"); // Look for a multi-node copy. String text = null; Set dobs = new HashSet (); MultiTransferObject mto = (MultiTransferObject) t.getTransferData (ExTransferable.multiFlavor); int count = mto.getCount (); for (int i = 0; i < count; i++) { Transferable ti = mto.getTransferableAt (i); Node n = NodeTransfer.node (ti, NodeTransfer.COPY); if (n != null) { String maybeText = isAcceptablePrimaryPaster (n); if (maybeText != null) { if (text != null) { //System.err.println(">1 primaries"); return; // tried to paste >1 primary at once } text = maybeText; } DataObject obj = (DataObject) n.getCookie (DataObject.class); if (obj != null) dobs.add (obj); if (maybeText == null && obj == null) { //System.err.println("useless node: " + n.getDisplayName ()); return; // useless node } } else { //System.err.println("not a node being pasted from " + ti); return; // not a node being pasted } } if (text != null) { // Got exactly one primary, all set. reallyMakePasteType (l, text, dobs); } else { //System.err.println("no primary found"); } } else { // Try a single-node copy. Node n = NodeTransfer.node (t, NodeTransfer.COPY); if (n != null) { //System.err.println("single-node flavor"); String text = isAcceptablePrimaryPaster (n); if (text != null) { DataObject dob = (DataObject) n.getCookie (DataObject.class); reallyMakePasteType (l, text, dob == null ? Collections.EMPTY_SET : Collections.singleton (dob)); } else { //System.err.println("not a primary"); } } else { //System.err.println("not a node flavor"); } } } catch (Exception e) { TopManager.getDefault ().notifyException (e); } } /** Attributes indicating files associated with an instance entry in the manifest. * The value of such an attribute should be a hex encoding of the serialized form * of a FileObject[]. */ static final Attributes.Name ATTR_FILEOBJS_HELPSET = new Attributes.Name ("X-APIWizard-HelpSet-AssociatedFiles"); static final Attributes.Name ATTR_FILEOBJS_INSTALL = new Attributes.Name ("X-APIWizard-Install-AssociatedFiles"); static final Attributes.Name ATTR_FILEOBJS_SECTION = new Attributes.Name ("X-APIWizard-Section-AssociatedFiles"); private static class HexBufferOutputStream extends OutputStream { private StringBuffer buf; private static final String digits = "0123456789ABCDEF"; public HexBufferOutputStream () { buf = new StringBuffer (); } public final void write (int b) throws IOException { buf.append (digits.charAt ((b & 0xF0) >> 4)); buf.append (digits.charAt (b & 0x0F)); } public String toString () { return buf.toString (); } public void close () throws IOException { buf = null; } } /** Actually add a paste type to the list. * @param l list of paste types to add to * @param text section name * @param dobs set of DataObject's to add contents of to JAR */ private void reallyMakePasteType (List l, final String text, final Set dobs) { //System.err.println("really making a paste type now for " + text); l.add (new PasteType () { public String getName () { return "Add " + text + " to " + (style == STYLE_SECTION ? "section" : "module"); } public Transferable paste () throws IOException { // First, serialize list of files, maybe. String associatedFiles; Set files; if (dobs.isEmpty ()) { associatedFiles = null; files = Collections.EMPTY_SET; } else { HexBufferOutputStream hex = new HexBufferOutputStream (); files = new HashSet (); Iterator it = dobs.iterator (); while (it.hasNext ()) { DataObject dob = (DataObject) it.next (); // XXX should maybe exclude any FileObject's which were already in the content files.add (dob.getPrimaryFile ()); } ObjectOutputStream oos = new ObjectOutputStream (hex); oos.writeObject (files.toArray (new FileObject[files.size ()])); oos.flush (); associatedFiles = hex.toString (); oos.close (); hex.close (); } // Now, write manifest. Manifest mani = provider.getManifest (); if (style == STYLE_MAIN) { mani.getMainAttributes ().put (type, text); if (associatedFiles != null) { if (type.equals (ModuleDescription.TAG_MAIN)) { mani.getMainAttributes ().put (ATTR_FILEOBJS_INSTALL, associatedFiles); } else { // TAG_DESCRIPTION mani.getMainAttributes ().put (ATTR_FILEOBJS_HELPSET, associatedFiles); } } } else { // STYLE_SECTION Attributes nue = new Attributes (); nue.put (ModuleDescription.TAG_SECTION_CLASS, type); if (associatedFiles != null) nue.put (ATTR_FILEOBJS_SECTION, associatedFiles); mani.getEntries ().put (text, nue); } provider.setManifest (mani); provider.addFiles (files); return ExTransferable.EMPTY; } }); } public boolean canCopy () { return false; } public boolean canCut () { return false; } public boolean canRename () { return false; } public boolean canDestroy () { return false; } public static class CategoryChildren extends Children.Keys { ManifestProvider provider; Object type; int style; Collection keys = Collections.EMPTY_SET; private ChangeListener list; public CategoryChildren (ManifestProvider provider, Object type) { this.provider = provider; this.type = type; // style initialized afterwards provider.addChangeListener (WeakListener.change (list = new ChangeListener () { public void stateChanged (ChangeEvent ev) { updateKeys (); } }, provider)); } protected void addNotify () { updateKeys (); } void updateKeys () { Manifest mani; try { mani = provider.getManifest (); } catch (IOException ioe) { TopManager.getDefault ().notifyException (ioe); removeNotify (); return; } if (style == STYLE_MAIN) { String val = mani.getMainAttributes ().getValue ((Attributes.Name) type); if (val != null) setKeys (new Object[] { val }); else setKeys (new Object[] { }); } else if (style == STYLE_SECTION) { List l = new LinkedList (); java.util.Map entries = mani.getEntries (); Iterator it = entries.keySet ().iterator (); while (it.hasNext ()) { String name = (String) it.next (); Attributes attr = (Attributes) entries.get (name); String secclass = attr.getValue (ModuleDescription.TAG_SECTION_CLASS); if (secclass != null && secclass.equalsIgnoreCase ((String) type)) { l.add (name); } } setKeys (keys = l); } else { // XXX STYLE_DEP implement setKeys (keys = Collections.EMPTY_SET); } } protected void removeNotify () { setKeys (keys = Collections.EMPTY_SET); } protected Node[] createNodes (final Object key) { if (style == STYLE_MAIN || style == STYLE_SECTION) { String val = (String) key; if (type.equals (ModuleDescription.TAG_DESCRIPTION)) val = val.replace ('.', '/') + ".hs"; Node baseNode = null; FileObject fo = TopManager.getDefault ().getRepository ().findResource (val); if (fo != null) { try { DataObject dob = DataObject.find (fo); baseNode = dob.getNodeDelegate (); } catch (DataObjectNotFoundException donfe) { } } if (baseNode == null) { baseNode = new AbstractNode (Children.LEAF); baseNode.setName (val); baseNode.setDisplayName (val + " <missing>"); baseNode.setShortDescription ("Could not find the object " + val + " in the Repository."); } return new Node[] { new InstanceNode (provider, type, style, val, baseNode) }; } else { // XXX STYLE_DEP implement return new Node[] { }; } } } } /* * Log * 22 Gandalf 1.21 1/26/00 Jesse Glick Manifest handling * changed--now more dynamic, synched properly with open document as for * real file types. * 21 Gandalf 1.20 1/22/00 Jesse Glick Manifest files can now * be recognized, not just JARs. * 20 Gandalf 1.19 12/17/99 Jesse Glick JavaHelpDataObject was * removed. * 19 Gandalf 1.18 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 18 Gandalf 1.17 10/7/99 Jesse Glick File associations * mechanism changed by the JAR Packager. * 17 Gandalf 1.16 10/6/99 Jesse Glick Added table of contents, * anchored context help. * 16 Gandalf 1.15 10/6/99 Jesse Glick JAR packager content * model change. * 15 Gandalf 1.14 10/5/99 Jesse Glick * 14 Gandalf 1.13 9/30/99 Jesse Glick Package rename and misc. * 13 Gandalf 1.12 9/21/99 Jesse Glick Small bugfixes. * 12 Gandalf 1.11 9/20/99 Jesse Glick Slick group pasting * (required fix in ExplorerActions). * 11 Gandalf 1.10 9/20/99 Jesse Glick New resources package. * 10 Gandalf 1.9 9/16/99 Jesse Glick Package change. * 9 Gandalf 1.8 9/16/99 Jesse Glick Changed to work in * recent builds again; filter node deletion fix. * 8 Gandalf 1.7 9/14/99 Jesse Glick Mostly debugged pasting. * 7 Gandalf 1.6 9/13/99 Jesse Glick Pasting implemented but * untested (compiles at least). * 6 Gandalf 1.5 9/13/99 Jesse Glick Got deletion to work. * 5 Gandalf 1.4 9/13/99 Jesse Glick Minor fixes. * 4 Gandalf 1.3 9/13/99 Jesse Glick Sections should be * case-insenitive. * 3 Gandalf 1.2 9/13/99 Jesse Glick Compiles. * 2 Gandalf 1.1 9/13/99 Jesse Glick * 1 Gandalf 1.0 9/13/99 Jesse Glick * $ */